package com.liuhouse.strame;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamDemo {


    public static void main(String[] args) {
        //得到作者
//        List<Author> authors = getAuthors();
//        test01(authors);

        //常用操作
        //创建流
        //1.单列集合创建流
//        Stream<Author> stream = authors.stream();

        //2.数组创建流
//        testArray();

        //双列集合
//        testHashMap();

        //中间操作 filter
//        testFilter();

        //中间操作 map
//        testMap();

        //中间操作  distinct
//        testDistinct();

        //中间操作 sorted
//        testSorted();

//        testLimit();

        //中间操作  skip
//        testSkip();

        //中间操作  flatMap
//        testFlatMap1();

        //中间操作  flatMap   案例2
//        testFlat2();

//        testFlat3();

        //终结操作 foreach
//        testForeach();

        //终结操作 count
//        testCount();

        //终结操作 max
//        testMax();

        //终结操作 min
//        testMin();

        //终结操作  collect
//        testCollect();

        //终结操作  collect
//        testCollectSet();

        //终结操作  collect
//        testCollectMap();

        //终结操作  anyMatch
//        testAnyMatch();

        //终结操作  allMatch
//        testAllMatch();

        //终结操作  noneMatch
//        testNoMatch();

        //终结操作  findAny
//        testFindAny();

        //终结操作  findFirst
//        testFirst();

        //终结操作 reduce
//        testReduce1();

        //终结操作  reduce
//        testReduce02();

        //终结操作 reduce
//        testReduce03();

        //如果使用一个参数重载方法求最小值的代码如下
//        testReduce04();

//        testStream();


        //常用的默认方法
        testPredicate();

    }

    private static void testPredicate() {
        //常用的默认方法 and ,我们在使用Predicate接口的时候可能需要进行判断条件的拼接,而and方法相当于
        //使用 && 来拼接两个判断条件
        //打印作家中年龄大于17并且姓名长度大于1的作家
//        List<Author> authors = getAuthors();
//        Stream<Author> authorStream = authors.stream();
//        authorStream.filter(((Predicate<Author>) author -> {
//            //通过函数式接口 进行过滤   符合条件的就留着 不符合条件的就剔除
//            return author.getAge() > 17;
//        }).and(author -> author.getName().length() > 1))
//           .forEach(author -> System.out.println(author.getName()));


        //另外一种写法
        //打印作家中年龄大于17并且姓名长度大于1的作家
        List<Author> authors = getAuthors();
        Stream<Author> stream = authors.stream();
        stream.filter(author -> author.getAge() > 17 && author.getName().length() > 1)
                .forEach(author -> System.out.println(author.getName()));


    }

    private static void testStream() {
        //注意事项
        //惰性求值(如果没有终结操作,中间操作是不会得到执行的)
        //流是一次性的(一旦一个流对象经过一个终结操作之后,就不能再被使用)
        //不会影响原数据(我们在六中可以多数据做很多处理,但是正常情况下是不会影响原来集合中的元素的,
        // 这也是往往我们期望的)
        List<Author> authors = getAuthors();
        Stream<Author> stream = authors.stream();
        stream.map(author -> {
            author.setAge(author.getAge() + 20);
            return author;
        }).forEach(author -> System.out.println(author.getAge()));
//        stream.map(author -> author.getAge()).forEach(age -> System.out.println(age));
//        System.out.println(stream);
//        stream.map(author -> author.getAge()).forEach(age -> System.out.println(age));
    }

    private static void testReduce04() {
        //使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        //从源码中可以看出来,如果不传递初始值,会把数组中的第一个元素做为初始值
        Optional<Integer> reduce = authors.stream()
                .map(author -> author.getAge())
                .reduce((result, elem) -> result > elem ? elem : result);
        System.out.println(reduce);
    }

    //终结操作 reduce
    private static void testReduce03() {
        //使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        Integer reduce = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MAX_VALUE, (Integer result, Integer elem) -> result > elem ? elem : result
        );
        System.out.println(reduce);
    }

    private static void testReduce02() {
        //使用reduce求作者中年龄的最大值
        List<Author> authors = getAuthors();
        Integer reduce = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
        System.out.println(reduce);
    }

    private static void testReduce1() {
        //使用reduce求所有作者年龄的和
        List<Author> authors = getAuthors();
        Integer reduce = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(0, (result, elem_age) -> result + elem_age);
        System.out.println(reduce);
    }

    private static void testFirst() {
        //获取一个年龄最小的作家,并输出他的姓名
        List<Author> authors = getAuthors();
        Optional<Author> first = authors.stream()
                .sorted((o1, o2) -> o1.getAge() - o2.getAge()).findFirst();
        first.ifPresent(author -> System.out.println(author.getName()));
    }

    private static void testFindAny() {
        //获取任意一个年龄大于18的作家,如果存在就输出他的名字
        List<Author> authors = getAuthors();
        Optional<Author> optionalAuthor = authors.stream()
                .filter(author -> author.getAge() > 18)
                .findAny();
        optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
    }

    private static void testNoMatch() {
        //判断作家是否都没有超过100岁的
        List<Author> authors = getAuthors();
        boolean b = authors.stream()
                .noneMatch(author -> author.getAge() > 100);
        System.out.println(b);
    }


    //终结操作  allMatch
    private static void testAllMatch() {
        //判断是否所有的作家都是成年人
        List<Author> authors = getAuthors();
        boolean b = authors.stream()
                .allMatch(author -> author.getAge() >= 18);
        System.out.println(b);
    }

    private static void testAnyMatch() {
        //判断是否有年龄30岁以上的作家
        List<Author> authors = getAuthors();
        boolean b = authors.stream()
                .anyMatch(author -> author.getAge() > 15);
        System.out.println(b);
    }

    private static void testCollectMap() {
        //获取一个Map集合,map的key为作者名称,value为List<Book>
        List<Author> authors = getAuthors();
        Map<String, Object> collect = authors.stream()
                .distinct()
                .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
        System.out.println(collect);
    }

    private static void testCollectSet() {
        //获取一个所有书名的Set集合
        //javaSe基础   set集合是可以自动去重的
        List<Author> authors = getAuthors();
        Set<String> collect = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getName())
                .collect(Collectors.toSet());
        System.out.println(collect);
    }

    private static void testCollect() {
        //把当前流转换位一个集合
        //例子 ： 获取一个存放所有作者名字的list集合
        //以前不知道是什么意思  现在知道了
        List<Author> authors = getAuthors();
        List<String> collect = authors.stream()
                .map(author -> author.getName())
                .distinct()
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    private static void testMin() {
        List<Author> authors = getAuthors();
        Optional<Integer> min = authors.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .map(book -> book.getScore())
                .min((score1, score2) -> score1 - score2);
        System.out.println(min.get());
    }

    private static void testMax() {
        //获取这些作家所出书籍的最高分
        //Stream<Author> -> Stream<Book> -> Stream<Integer> score ->求最大值
        List<Author> authors = getAuthors();
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Stream.of(book.getScore()))
                .max((score1, score2) -> score1 - score2);
        System.out.println(max.get());
    }

    //终结操作  count
    private static void testCount() {
        //打印出这些作家所出书籍的数目,注意删除重复元素
        List<Author> authors = getAuthors();
        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);

    }


    //终结操作 foreach
    private static void testForeach() {
        //对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历的元素进行什么具体操作
        //1.例子,输出所有作家的名字
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .map(author -> author.getName())
                .distinct()
                .forEach(name -> System.out.println(name));
    }

    private static void testFlat3() {
        //打印现有数据的所有分类,要求对分类进行去重,不能出现这种格式,哲学,爱情
        List<Author> authors = getAuthors();
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(category -> System.out.println(category));
    }

    private static void testFlat2() {
        //打印现有数据的所有分类,要求对分类进行去重,不能出现这种格式 : 哲学,爱情  要求必须是全部去重的
        List<Author> authors = getAuthors();

        //使用原始的方式实现一下,虽然有更方便的方式,但是使用原始的方式会也是要掌握的
        //毕竟在别的语言中没有这个  所以  掌握这个是必须的  而且对比一下方便度
        ArrayList<Book> books_list = new ArrayList<>();
        for(Author author : authors){
            List<Book> books = author.getBooks();
            for (Book book : books){
                //这里对每个对象要进行去重 就很复杂了 真的很复杂了  所
                //咱们这里有一个要求,所以这里手动去重的时候需要有一个标准,只要是认真id是一样的
                //就认真是同一本书  实际的工作中也是这样的
                books_list.add(book);
            }
        }

        ArrayList<Book> books_union_list = new ArrayList<>();
        ArrayList<Long> arr = new ArrayList<>();
        ArrayList<String> cate_list = new ArrayList<>();
        for (Book book : books_list){
            if(arr.size() > 0){
                if(!containElement(arr , book.getId())){
                    books_union_list.add(book);
                    arr.add(book.getId());
                    cate_list.add(book.getCategory());
                }
            }else{
                books_union_list.add(book);
                arr.add(book.getId());
                //可以在循环中一个一个处理 然后返回  但是在循环中处理这些事情真的很麻烦
                //所以 这就就全部给一个数组 然后再外面统一处理  比较好
                cate_list.add(book.getCategory());
            }
        }

        //循环书籍分类
        ArrayList<String> cate_union_list = new ArrayList<>();
        for (String cate : cate_list){
            String[] split_cate = cate.split(",");
            for(String sp : split_cate){
                if(cate_union_list.size() > 0){
                    if(containElementString(cate_union_list, sp) == false){
                        cate_union_list.add(sp);
                    }
                }else{
                    cate_union_list.add(sp);
                }
            }
        }

        System.out.println(cate_union_list);


    }


    //判断数组中是否包含某个元素
    public static boolean containElement(List<Long> array , Long target){
        for(Long i : array){
            if(i == target){
                return true;//如果存在目标元素,则返回true
            }
        }
        return false;
    }


    public static boolean containElementString(List<String> array , String target){
        for(String i : array){
            if(i.equals(target)){
                return true;//如果存在目标元素,则返回true
            }
        }
        return false;
    }


    //中间操作  flatMap
    private static void testFlatMap1() {
        //打印所有书籍的名字,要求对重复的元素进行去重
//        List<Author> authors = getAuthors();
//        authors.stream()
//                .flatMap(new Function<Author, Stream<Book>>() {
//                    @Override
//                    public Stream<Book> apply(Author author) {
//                        return author.getBooks().stream();
//                    }
//                })
//                .distinct()
//                .forEach(new Consumer<Book>() {
//                    @Override
//                    public void accept(Book book) {
//                        System.out.println(book.getName());
//                    }
//                });


        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .forEach(book -> System.out.println(book.getName()));

    }


    //中间操作  skip
    private static void testSkip() {
        //打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄排序
        List<Author> authors = getAuthors();
        //其实这些方法都有对应的实现方法  是系统内部实现的  咱只需要会用就行了
        //其实核心逻辑就是不断的把列表进行筛选,后面的操作都是使用前面筛选过的数据
        authors.stream()
                .distinct()
                .sorted()
                .skip(1)
                .forEach(author -> System.out.println(author.getAge() + author.getName()));
    }


    //中间操作  limit 返回指定的条数
    private static void testLimit() {
        //返回去重之后的两个作者的数据
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted()
                .limit(1)
                .forEach(author -> System.out.println(author.getName()));
    }


    private static void testSorted() {
        List<Author> authors = getAuthors();
//        authors.stream().sorted().forEach(author -> System.out.println(author.getAge()));
        authors.stream().sorted((o1, o2) -> {
//                return o2.getAge().compareTo(o1.getAge());
            return o2.getAge() - o1.getAge();
        }).forEach(author -> System.out.println(author.getAge()));

    }

    //元素去重
    private static void testDistinct() {
        List<Author> authors = getAuthors();
        //这里需要注意 distinct 方法是依赖Object的equals方法来判断是否是相同的对象
        //所以需要注意重写equals方法
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author));
    }

    //中间操作 map
    private static void testMap() {
        //可以对流中的元素进行计算或者转化
        List<Author> authors = getAuthors();
//        authors.stream().forEach(author -> System.out.println(author.getName()));
//        authors.stream()
//                .map(author -> author.getName())
//                .forEach(s -> System.out.println(s));

        //给所有的作者年龄+20
//        authors.stream()
//                .map(author -> author.getAge())
//                .map(age -> age+20)
//                .forEach(age -> System.out.println(age));

//        authors.stream()
//                .map(author -> {
//                    //给所有的作者的年龄添加20
//                    Integer age = author.getAge();
//                    //设置到原来的对象中去
//                    author.setAge(age + 20);
//                    return author;
//                }).forEach(author -> System.out.println(author));

        authors.stream()
                .map(author -> {
                    author.setAge(author.getAge() + 20);
                    return author;
                }).forEach(author -> System.out.println(author));


    }

    //中间操作 - filter
    static void testFilter() {
        //filter 可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中
        //打印所有作家姓名的长度大于3的作家的姓名
        List<Author> authors = getAuthors();
        Stream<Author> stream = authors.stream();
        stream.filter((Author author) -> {
            return author.getName().length() > 3;
        }).forEach(author -> System.out.println(author.getName()));
    }

    //双列集合
    private static void testHashMap() {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("蜡笔小新" , 19);
        map.put("狗仔队" , 17);
        map.put("DNF" , 18);
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        Stream<Map.Entry<String, Integer>> stream = entries.stream();
        stream.filter(stringIntegerEntry -> stringIntegerEntry.getValue() > 17
        ).forEach(stringIntegerEntry -> System.out.println(stringIntegerEntry.getKey() + ":" + stringIntegerEntry.getValue() + "岁"));
    }

    private static void testArray() {
        Integer[] arr = {1,2,3,4,5 ,5 , 1};
//        Stream<Integer> stream = Arrays.stream(arr);
        Stream<Integer> stream = Stream.of(arr);
        stream.distinct().forEach(integer -> System.out.println(integer));
    }

    private static void test01(List<Author> authors) {
        //一个需求：打印集合中所有年龄小于18岁的作家的名字,并且要注意去重
//        authors.stream()//把集合转换成流
//                .distinct()
//                .filter(new Predicate<Author>() {
//                    @Override
//                    public boolean test(Author author) {
//                        return author.getAge() < 18;
//                    }
//                }).forEach(new Consumer<Author>() {
//                    @Override
//                    public void accept(Author author) {
//                        System.out.println(author.getName());
//                    }
//                });
//        ;

        authors.stream()//把集合转换成流
                .distinct()
                .filter(author -> author.getAge() < 18)
                .forEach(author -> System.out.println(author.getName()));
        ;
    }


    private static List<Author> getAuthors(){
        //数据初始化
        Author author1 = new Author(1L, "吴承恩111", 18, "字汝忠，号射阳居士、射阳山人", null);
        Author author2 = new Author(2L, "施耐庵222", 18, "名霑，字梦阮，号雪芹，又号芹圃、芹溪。祖籍辽阳。清朝小说家、诗人、画家。曹寅之孙", null);
        Author author3 = new Author(1L, "吴承恩111", 20, "字汝忠，号射阳居士、射阳山人", null);
        Author author4 = new Author(4L, "罗", 25, "名本，字贯中， [18]号湖海散人， [16-17]太原人。元末明初小说家", null);

        //书籍列表
        ArrayList<Book> books1 = new ArrayList<>();
        ArrayList<Book> books2 = new ArrayList<>();
        ArrayList<Book> books3 = new ArrayList<>();

        //给书籍添加数据
        books1.add(new Book(1L , "刀的两侧是光明与黑暗11","哲学,爱情11" ,85 ,"一刀就会两段啊11"));
        books1.add(new Book(2L , "刀的两侧是光明与黑暗22","个人成长,爱情22" ,90 ,"一刀就会两段啊222"));

        books2.add(new Book(3L , "刀的两侧是光明与黑暗33","个人成长,爱情33" ,91 ,"一刀就会两段啊333"));
        books2.add(new Book(4L , "刀的两侧是光明与黑暗44","个人成长,爱情44" ,60 ,"一刀就会两段啊444"));
        books2.add(new Book(5L , "刀的两侧是光明与黑暗55","个人成长,爱情55" ,50 ,"一刀就会两段啊555"));

        books3.add(new Book(6L , "刀的两侧是光明与黑暗66","个人成长,爱情66" ,50 ,"一刀就会两段啊666"));
        books3.add(new Book(6L , "刀的两侧是光明与黑暗77","个人成长,爱情77" ,88 ,"一刀就会两段啊777"));
        books3.add(new Book(8L , "刀的两侧是光明与黑暗88","个人成长,爱情88" ,30 ,"一刀就会两段啊888"));

        //给作者分配图书
        author1.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books1);
        author4.setBooks(books2);

        //将作者进行合并 ， 合并成一个数组
        List<Author> authorList = new ArrayList<>(Arrays.asList(author1 , author2 , author3 , author4));
        return authorList;
    }

}
